<?php

declare(strict_types=1);
/**
 * 
 */
namespace WanZ\App\User\Logic;

use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Context\Context;
use Hyperf\Redis\RedisFactory;
use WanZ\App\User\Logic\Traits\UserTrait;
use WanZ\App\Utils\Platform\Ali\AliFactory;
use WanZ\App\User\Logic\CompanyLogic;
use Alipay\EasySDK\Kernel\Factory;
use Alipay\EasySDK\Kernel\Config;
use MoChat\Framework\Constants\ErrorCode;
use MoChat\Framework\Exception\CommonException;

/**
 * Class AliLogic
 * @package WanZ\App\User\Logic
 */
class AliLogic
{
	use UserTrait;

    /**
     * @Inject
     * @var StdoutLoggerInterface
     */
    private $logger;	
    /**
     * @Inject
     * @var CompanyLogic
     */
    private $companyLogic;

	protected $loginKeyPrefix = "temp:login:ali:";

	protected $inited = false;

	protected $config = [];

    /**
	 * 
     * @throws CommonException
     */
    protected function initFactory(int $companyId = 0): void
	{
	    $this->config = $this->companyLogic->getPayConfig($companyId);

		$options = new Config();
		$options->protocol = 'https';
		$options->gatewayHost = 'openapi.alipay.com';
		$options->signType = 'RSA2';

		$options->appId = $this->config['aliMiniAppId'];

		// 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
		$options->merchantPrivateKey = $this->config['aliMiniSecret'];

		$path = BASE_PATH . '/storage/cert/alipay/' . $companyId . '/';

        //'<-- 请填写您的支付宝公钥证书文件路径，例如：/foo/alipayCertPublicKey_RSA2.crt -->';
		$options->alipayCertPath =  $path . 'alipayCertPublicKey_RSA2.crt';
		$options->alipayRootCertPath = $path . 'alipayRootCert.crt';
		$options->merchantCertPath = $path . 'appCertPublicKey.crt';

		if (!is_dir($path)) {
			mkdir($path, 0777, true);
		}

		if (!file_exists($options->alipayRootCertPath)) {
			file_put_contents($options->alipayRootCertPath, $this->config['aliRootCert'], LOCK_EX);
		}

		if (!file_exists($options->merchantCertPath)) {
			file_put_contents($options->merchantCertPath, $this->config['aliMerchantCert'], LOCK_EX);
		}

		if (!file_exists($options->alipayCertPath)) {
			file_put_contents($options->alipayCertPath, $this->config['aliCert'], LOCK_EX);
		}

		//注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
		// $options->alipayPublicKey = '<-- 请填写您的支付宝公钥，例如：MIIBIjANBg... -->';

		//可设置异步通知接收服务地址（可选）
		//$options->notifyUrl = "<-- 请填写您的支付类接口异步通知接收服务地址，例如：https://www.test.com/callback -->";

		//可设置AES密钥，调用AES加解密相关接口时需要（可选）
		//$options->encryptKey = "";
		AliFactory::setOptions($options);
		// Factory::setOptions($options);
	}

    /**
     * 通过CODE获取ALI的UID
     * @param string $authCode
     * @param int $companyId
     * @return string
     * @throws CommonException
     */
	protected function getAliUserId(string $authCode, int $companyId): string
	{
		try {
			$uid = $this->getUser($authCode, $companyId, false)['u_id'] ?? '';
		} catch (\Throwable $e) {
			$uid = '';
		}
		if (!empty($uid)) return $uid;

		$this->initFactory($companyId);

		try {
			$ret = AliFactory::base()->oauth()->getToken($authCode);
			if ((int) $ret->code > 0) {
                throw new CommonException((int) $ret->code, '获取用户支付宝信息失败  '.$ret->subCode. '  '.$ret->subMsg);
			}

			$uid = $ret->userId;
			$data['u_id'] = $uid;
			$data['refresh_token'] = $ret->refreshToken;
			$data['expires_in'] = $ret->expiresIn;

			$cache = make(RedisFactory::class)->get('default');
			$key = $this->loginKeyPrefix . $companyId . ':' . $authCode;
			$cache->set($key, json_encode($data, 256));
			$cache->expire($key, (int)config('login.bind_time', 240));

		} catch (\Throwable $e) {
		    $this->logger->error('getAliUserIdError', [
		        'errorMsg'  => $e->getMessage(),
                'errorCode' => $e->getCode(),
                'site' => $e->getFile() .'|'. $e->getLine(),
                'uid'  => $uid ?? 0,
                'requestData'  => json_encode([
                    'auth_code' => $authCode,
                    'company_id' => $companyId,
                    'platform' => 'bicycles'
                ]),
                'responseData' => $ret ? json_encode($ret) : '',
                'traceid'      => Context::get('traceid', ''),
                'client_tranceid' => Context::get('client_traceid', ''),
            ]);

		    //$this->alarmHandle(['code' => $e->getCode()], ['log_tag' => 'getAliUserIdError']);
		    throw new CommonException($e->getCode(), '获取用户信息失败' . $e->getMessage());
		}

		return $uid;
	}

    /**
     * 支付宝登录
     * @param string $authCode
     * @param int $companyId
     * @return array
     * @throws CommonException
     */
	public function login(string $authCode, int $companyId): array
	{
		if (empty($authCode) || $companyId <= 0) throw new CommonException(ErrorCode::INVALID_PARAMS, '登录失败，缺少参数');
		$user = $this->getAliUserId($authCode, $companyId);

		return $this->getByAliMiniOpenId($user);
	}

	/**
	 * 从缓存里面获取auth_code对应的user信息
	 * @param string $authCode
	 * @param int $companyId
     * @param boolean $throw
	 * @return array
	 * @throws CommonException
	 */
	public function getUser(string $authCode, int $companyId, bool $throw = true): array
	{
		$cache = make(RedisFactory::class)->get('default');
		$key = $this->loginKeyPrefix . $companyId . ':' . $authCode;
		$user = json_decode((string) $cache->get($key), true);
		if (empty($user) || !isset($user['u_id']) || empty($user['u_id'])) {
            if ($throw) {
                throw new CommonException(ErrorCode::SERVER_ERROR, '绑定手机超时，请重新登录小程序后再绑定');
            }
            return [];
		}

		return $user;
	}

	/**
	 * 解析前端给的加密的用户信息
	 * @param string $encryptedData
	 * @param string $sign
	 * @param int $companyId
	 * @return array
	 * @throws CommonException
	 */
	public function decryptMiniUserInfo(string $encryptedData = '', int $companyId = 0): array
	{
		$this->initFactory($companyId);
		$sign = $this->config['aliAesSecret'];
		$encryptedData = str_replace(" ", "+", $encryptedData);

		//ESHelper::logInfo(__FUNCTION__, ["sign" => $sign, "en_data" => $encryptedData]);

		$aesKey = base64_decode($sign);
		$aesCipher = base64_decode($encryptedData);
		$result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1);
		if (empty($result)) throw new CommonException(ErrorCode::SERVER_ERROR, '解析加密数据失败');
		$data = json_decode($result, true);
		if (empty($data) || $data['msg'] != 'Success') throw new CommonException(ErrorCode::SERVER_ERROR, '解析加密数据失败');
		return $data;
	}

    /**
     * @param $errorMsg
     * @param $reqData
     */
    // private function alarmHandle(array $errorMsg, array $reqData)
    // {
    //     \Swoft::trigger('error_alarm', null, [
    //         'type'     => 'aliLogin',
    //         'errorMsg' => $errorMsg,
    //         'params'   => $reqData,
    //     ]);
    // }
}
